#include <iostream>
#include <stdexcept>
#include "Wrappers/Pthread/Thread.hpp"
#include "Wrappers/Pthread/ThreadException.hpp"

namespace wrappers {
namespace pthread {

Thread::Thread() {
  mThread = 0;
  mCallable = NULL;
  mArgument = NULL;
  mState = STANDING;
}

Thread::Thread(Callable callable, void *argument) {
  mThread = 0;
  mCallable = callable;
  mArgument = argument;
  mState = STANDING;
  Run();
}

Thread::~Thread() {

}

void Thread::Run() {
  if (mState == STANDING) {
    if (!mCallable)
      throw std::invalid_argument("Thread: callable can not be null");
    if (pthread_create(&mThread, NULL, mCallable, mArgument) != 0)
      throw ThreadException("Thread::Run failed to create the thread");
    mState = RUNNING;
  }
  else {
    switch (mState) {
    case RUNNING:
      throw ThreadException("Thread::Run the thread is already running");
      break;
    case DETACHED:
      throw ThreadException("Thread::Run the thread is detached");
      break;
    case TERMINATED:
      throw ThreadException("Thread::Run the thread has terminated");
      break;
    default:
      break;
    }
  }
}

void Thread::Cancel() {
  if (mState == RUNNING) {
    pthread_cancel(mThread);
    mState = TERMINATED;
  }
}

void Thread::Kill() {
  Cancel();
}

void Thread::Detach() {
  mState = DETACHED;
}

bool Thread::Join() {
  if (mState == RUNNING) {
    if (pthread_join(mThread, NULL) != 0)
      throw ThreadException("Thread::Join failed to join the thread");
    mState = TERMINATED;
    return true;
  }
  return false;
}

void Thread::SetCallable(Callable callable) {
  mCallable = callable;
}

void Thread::SetCallableArgument(void *argument) {
  mArgument = argument;
}

}
}
